use std::{
    net::SocketAddr,
    path::{Path, PathBuf},
    sync::Arc,
};

use askama_axum::Response;
use askama_axum::Template;
use axum::{
    extract::{ConnectInfo, Request, State},
    response::{Html, IntoResponse},
    routing::get,
    Router,
};
use chrono::{DateTime, Local};
use tokio::{
    io::AsyncReadExt,
    net::TcpListener,
    sync::broadcast::{Receiver, Sender},
};

#[derive(Debug, Clone)]
pub enum Message {
    Shutdown,
    Log(String),
}

#[derive(Clone, Debug)]
pub struct HttpServer {
    pub http_state: HttpState,
    pub sender: Sender<Message>,
    pub receiver: Arc<Receiver<Message>>,
}

impl Default for HttpServer {
    fn default() -> Self {
        HttpServer::new("0.0.0.0:8080".parse().unwrap(), "./".into())
    }
}

impl HttpServer {
    pub fn new(server_addr: SocketAddr, server_path: PathBuf) -> Self {
        let http_state = HttpState::new(server_addr, server_path, true, "index.html".to_string());
        let (sender, receiver) = tokio::sync::broadcast::channel(16);
        let receiver = Arc::new(receiver);
        Self {
            http_state,
            sender,
            receiver,
        }
    }
    async fn handler(
        State(state): State<Arc<HttpServer>>,
        ConnectInfo(addr): ConnectInfo<SocketAddr>,
        request: Request,
    ) -> impl IntoResponse {
        let http_server = state.clone();
        let sender = http_server.sender.clone();

        // let start = Instant::now();
        let method = request.method().to_string();
        let uri = request.uri().to_string();
        let version = request.uri().scheme_str().unwrap_or("http");
        let user_agent = request
            .headers()
            .get("user-agent")
            .map(|header| header.to_str().unwrap_or("-"))
            .unwrap_or("-");
        let status_code = 200; // 假设状态码为200 OK

        let state = http_server.http_state.clone();
        let current_path = state.server_path.clone();
        let current_uri_path: PathBuf = request.uri().path().parse().unwrap();

        let current_path = current_path.join(current_uri_path.strip_prefix("/").unwrap());
        let current_path = percent_encoding::percent_decode_str(current_path.to_str().unwrap())
            .decode_utf8_lossy();
        let current_path = PathBuf::from(std::str::from_utf8(current_path.as_bytes()).unwrap());

        if current_path.is_file() {
            let mut file = tokio::fs::File::open(current_path).await.unwrap();
            let mut buffer = Vec::new();
            if file.read_to_end(&mut buffer).await.is_err() {
                return "文件读取错误".into_response();
            }
            return Response::builder()
                .header("Content-Type", "application/octet-stream")
                .body(buffer.into())
                .unwrap();
        } else if current_path.is_dir() {
            let mut dir_entrys_vec = vec![Item {
                path: current_uri_path
                    .parent()
                    .unwrap_or_else(|| Path::new(""))
                    .to_string_lossy()
                    .to_string(),
                name: "..".to_string(),
                file_type: "-".to_string(),
                time: "-".to_string(),
                size: "-".to_string(),
            }];
            let mut dir_entrys = current_path.read_dir().unwrap();
            while let Some(dir_entry) = dir_entrys.next() {
                match dir_entry {
                    Ok(dir_entry) => {
                        let file_name = dir_entry.file_name().into_string().unwrap();
                        let file_path = current_uri_path.clone().join(file_name.clone());
                        let file_size = dir_entry.metadata().unwrap().len();
                        let mtime = dir_entry.metadata().unwrap().modified().unwrap();
                        let mtime: DateTime<chrono::Local> = mtime.into();
                        let mtime = mtime.format("%Y-%m-%d %H:%M:%S").to_string();
                        let item = Item {
                            path: file_path.to_string_lossy().to_string(),
                            name: file_name,
                            file_type: format!(
                                "{}",
                                if dir_entry.file_type().unwrap().is_dir() {
                                    "文件夹"
                                } else {
                                    "文件"
                                }
                            ),
                            size: format_file_size(file_size),
                            time: mtime,
                        };
                        dir_entrys_vec.push(item);
                    }
                    Err(err) => {
                        println!("error:{:?}", err);
                    }
                }
            }

            let html_template = HtmlTemplate {
                items: dir_entrys_vec,
            };

            // let response_time_ms = start.elapsed().as_millis();
            // 构建日志条目

            let log_message = format!(
                "{addr} - - [{date}] \"{method} {uri} {version}\" {status_code} - \"{user_agent}\"",
                date = Local::now().format("%d/%b/%Y:%H:%M:%S %z")
            );

            sender.send(Message::Log(log_message)).unwrap();

            let html = html_template.render().unwrap();
            return Html(html).into_response();
        } else {
            return Html("not found".to_string()).into_response();
        }
    }
    pub async fn start_server(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let listener = TcpListener::bind(self.http_state.server_addr).await;
        let listener = match listener {
            Ok(listener) => {
                let msg = format!(
                    "[{}] 服务已启动，正在监听地址：{}",
                    DateTime::<chrono::Local>::from(std::time::SystemTime::now())
                        .format("%Y-%m-%d %H:%M:%S"),
                    self.http_state.server_addr
                );
                self.sender.send(Message::Log(msg)).unwrap();
                listener
            }
            Err(e) => {
                let msg = format!(
                    "[{}] 服务启动失败，错误信息：{}",
                    DateTime::<chrono::Local>::from(std::time::SystemTime::now())
                        .format("%Y-%m-%d %H:%M:%S"),
                    e
                );
                self.sender.send(Message::Log(msg)).unwrap();
                return Err(e.into());
            }
        };

        let app = Router::new()
            .fallback(get(HttpServer::handler))
            .with_state(Arc::new(self.clone()));

        axum::serve(
            listener,
            app.into_make_service_with_connect_info::<SocketAddr>(),
        )
        .with_graceful_shutdown({
            let mut receiver = self.receiver.resubscribe();
            let sender = self.sender.clone();
            async move {
                while let Ok(msg) = receiver.recv().await {
                    match msg {
                        Message::Shutdown => {
                            sender
                                .send(Message::Log(format!(
                                    "Server shutdown at {}",
                                    DateTime::<chrono::Local>::from(std::time::SystemTime::now())
                                        .format("%Y-%m-%d %H:%M:%S")
                                )))
                                .unwrap();
                            break;
                        }
                        _ => {
                            continue;
                        }
                    }
                }
            }
        })
        .await?;

        Ok(())
    }
}

#[derive(Debug, Clone)]
pub struct HttpState {
    pub server_addr: SocketAddr,
    pub server_path: PathBuf,
    pub allow_file_listing: bool,
    pub index: String,
}

impl HttpState {
    pub fn new(
        server_addr: SocketAddr,
        server_path: PathBuf,
        allow_file_listing: bool,
        index: String,
    ) -> Self {
        Self {
            server_addr,
            server_path,
            allow_file_listing,
            index,
        }
    }
    pub fn set_server_path(&mut self, server_path: PathBuf) {
        self.server_path = server_path;
    }
    pub fn set_server_addr(&mut self, server_addr: SocketAddr) {
        self.server_addr = server_addr;
    }

    pub fn set_allow_file_listing(&mut self, allow_file_listing: bool) {
        self.allow_file_listing = allow_file_listing;
    }

    pub fn set_index(&mut self, index: String) {
        self.index = index;
    }
}

#[derive(Template)]
#[template(path = "index.html")]
struct HtmlTemplate {
    items: Vec<Item>,
}

struct Item {
    path: String,
    name: String,
    file_type: String,
    size: String,
    time: String,
}

fn format_file_size(size: u64) -> String {
    if size <= 1024 {
        format!("{} bytes", size)
    } else {
        let size_kb = size as f64 / 1024.;
        if size_kb <= 1024. {
            format!("{:.2} KiB", size_kb)
        } else {
            let size_mb = size_kb / 1024.;
            if size_mb <= 1024. {
                format!("{:.2} MiB", size_mb)
            } else {
                let size_gb = size_mb / 1024.;
                format!("{:.2} GiB", size_gb)
            }
        }
    }
}
